#!/usr/bin/python3
# -*- coding:utf-8 -*-
""""
@Author: lijk34925
@Time: 2024/3/7 9:49
@File: common_test_prepare.py
"""
import os
import re
import traceback
import time

from qturboframe.lib.utils.by import By
from qturboframe.lib.webdriver import SeleniumError
from qturboframe.lib.utils.variable import Variable

from BladeFerm.databases.DealSql.EnvConfig import EnvConfig
from BladeFerm.databases.DealSql.Unisql import Unisql
from BladeFerm.hui2_app.basic import sys_login
from BladeFerm.utils.FileOerate import getFilePath
from BladeFerm.databases import DB_APP
from BladeFerm.Lib.Common import logger
from BladeFerm.hui2_app import FE
from BladeFerm.hui2_app.basic import open_child_system

from HGScripts.HGConfig import SysConfig

BladeVariable = Variable()


def get_blade_param(name="", default=""):
    """
    获取blade平台的参数
    :param name:
    :param default:
    :return:
    """
    result = BladeVariable.get_variable(name)
    if result and result != 'AGENT_EMPTY':
        result_last = result
    else:
        result_last = default
    logger.info(f"get_blade_param: {name} = {result_last}")
    return result_last


def check_result(result: dict, check_servers=None):
    """
    断言服务启动结果
    :param result: 检查结果
    :param check_servers: 要检查的服务名称，为空时默认检查所有服务！
    :return:
    """
    if not check_servers:
        check_servers = ",".join(list(result.keys()))
    for key, value in result.items():
        if not value and re.search(key, check_servers, re.I):
            error = f"{key} 服务重启失败！不适宜继续后续测试流程"
            logger.error(error)
            raise SeleniumError(error)


def test_db_link(func):
    """
    装饰器：测试数据库连接
    :param func:
    :return:
    """
    def wrapper(*args, **kwargs):
        BV = Variable()
        system_env = BV.get_variable('varg_db_type')
        init_env = SysConfig.get('env')
        system_env_real = system_env if system_env != 'AGENT_EMPTY' and system_env else init_env
        try:
            if re.search("pg", system_env_real, re.I):
                sql_status = "select 1"
            else:
                sql_status = "select 1 from dual"
            DB_APP.run_one_sql(sql_status)
            # 执行脚本
            func(*args, **kwargs)
        except SystemExit as e:
            logger.info(f"脚本被终止调用成功！TIP:{e}")
        except Exception as e:
            error_info = traceback.format_exc()
            pattern = f"java.sql.SQLNonTransientConnectionException|connection closed"
            if re.search(pattern, str(error_info), re.I):
                logger.warning(f"数据库连接异常，重新连接数据库")
                system_odbc_config = SysConfig.get(system_env_real).get('databases')
                system_odbc_config.update({'env': system_env_real})
                logger.info(f'Init db link! system_odbc_config: {system_odbc_config}')
                DB_APP.make_db(**system_odbc_config)
            else:
                logger.error(f"format_exc: {error_info}")
                raise SeleniumError(f"UI test error: {e}")
        finally:
            logger.info("数据库连接检查OK！")
    return wrapper


# 执行sql脚本写入
@test_db_link
def sql_handle_class(env, path):
    act_sql_list = list()
    if path and os.path.isfile(path):
        fp, fn = os.path.split(path)
        fn_name, ext = fn.split('.')
        v_fn_name = "varg_sql_filename"
        sql_act_filename = BladeVariable.get_variable(v_fn_name)
        if sql_act_filename and sql_act_filename == fn_name:
            logger.info(f"已经运行过sql文件：{path}")
        else:
            BladeVariable.set_variable(v_fn_name, fn_name)
            # blade_exec_sql(path=path, env=env)  # 运行sql文件插入
            # sql_list = read_sql_file(filename=path)
            sql_list = Unisql(path=path, env=env, config=SysConfig)
            act_results = DB_APP.get_run_sql_result(sql_list)
            act_sql_list = sql_list
            logger.info(f"执行sql文件：{path} 完成！")
            # 检查sql执行结果 若存在失败则Fail
            act_sql_result_flag = True
            for index, result in act_results.items():
                if len(result) > 0 and result[0].get('ERROR_CODE', None):
                    err_info = result[0].get('ERROR_INFO', '')
                    logger.error(err_info)
                    act_sql_result_flag = False
            if not act_sql_result_flag:
                raise SeleniumError("SQL执行存在失败，请检查！")
            else:
                logger.info("SQL脚本执行全部Pass！")
    else:
        logger.warning(f"目录：{path} 为空，不获取SQL文件！")
    return act_sql_list


def login_web_page():
    """
    登录web页面：从blade的环境配置获取地址信息
    :return:
    """
    varb = Variable()
    env = varb.get_variable('varg_db_type')
    current_path = ""
    if env and env != 'AGENT_EMPTY':
        current_path = varb.get_variable('varu_CurrentPath')
        res = re.search(r"data\\(.*?)\\(.*?)\\标准数据", current_path)
        project_name = res.group(1) if res else ""
        ENV = EnvConfig(env, config=SysConfig)
        url = ENV.url
        user = ENV.user
        pwd = ENV.pwd
        logger.info(f"blade环境信息：{env}, 项目名称: {project_name}, url: {url}, user: {user}, pwd: {pwd}")
        # 记录执行次数，超过一定次数后，以清除页面的缓存，防止长时间运行用例导致浏览器崩溃
        run_count = varb.get_variable('varg_run_count')
        logger.debug(f"varg_run_count: {run_count}")
        if run_count == 'AGENT_EMPTY' or not run_count:
            sys_login(user, pwd, url)
            varb.set_variable('varg_run_count', '0')
        elif run_count and int(run_count) > 50:
            sys_login(user, pwd, url)
            varb.set_variable('varg_run_count', '0')
        else:
            varb.set_variable('varg_run_count', str(int(run_count) + 1))
    return env, current_path


# 装饰器，处理入参，sql写入，浏览器登录操作等
def common_test_prepare(func):
    def test_data_prepare(input_data_raw, env, current_path):
        # 检查是否需要执行sql脚本
        if "指标编号" in list(input_data_raw.keys()):
            task_name = input_data_raw.get('任务编号')
            project_web_path = os.path.join(current_path, "数据预埋\\证券风控")
            sql_path = getFilePath(str(task_name) + '.*\.sql', project_web_path)
            sql_list = sql_handle_class(env, sql_path)
            return sql_list
        else:
            project_web_path = os.path.join(current_path, "数据预埋\\证券风控")
            case_sql_path = input_data_raw.pop('脚本路径', '')
            case_sql_name = input_data_raw.get('入口', '')
            if case_sql_name:
                project_web_path = os.path.join(project_web_path, case_sql_path)
                case_sql_name = case_sql_name.split('-')[-1]
                sql_path = getFilePath(f'.*?{case_sql_name}.*?\.sql', project_web_path)
                logger.info(f'sql_script_path:{sql_path}')
                sql_list = sql_handle_class(env, sql_path)
                return sql_list
            else:
                return []

    # 测试公共准备工作，数据读取，UI登录
    def wrapper(*args, **kwargs):
        logger.debug(f"wrapper:{args} {kwargs}")
        if args:
            input_data = args[0]
            ui_expect = args[1]
        else:
            input_data = kwargs.get('input_data')
            ui_expect = kwargs.get('ui_expect')
        if ui_expect.get('result', None):
            ui_expect.pop('result', None)
        if ui_expect.get('resp_status_code', None):
            ui_expect.pop('resp_status_code', None)
        logger.info(f'入参：{input_data}')
        logger.info(f'期望：{ui_expect}')
        delete_sql_list = list()
        try:
            env, current_path = login_web_page()
            if current_path:
                # 测试数据准备
                sql_list = test_data_prepare(input_data, env, current_path)
                if sql_list:
                    delete_sql_list = [re.match(r"delete ", sql, re.I | re.M) for sql in sql_list]
            # 执行脚本
            func(*args, **kwargs)
        except SystemExit as e:
            logger.info(f"脚本被终止调用成功！TIP:{e}")
        except Exception as e:
            logger.error(f"error: {e}")
            error_info = traceback.format_exc()
            logger.error(f"format_exc: {error_info}")
            raise SeleniumError(f"UI test error: {e}")
        finally:
            if delete_sql_list:
                logger.info(f"执行删除sql脚本:{delete_sql_list}")
                DB_APP.get_run_sql_result(delete_sql_list)
            logger.info("脚本执行完成！")
    return wrapper


def catch_exceptions(func):
    def wrapper(*args, **kwargs):
        try:
            func(*args, **kwargs)
        except SystemExit as e:
            logger.info(f"脚本被终止调用成功！TIP:{e}")
        except Exception as e:
            logger.error(f"error: {e}")
            error_info = traceback.format_exc()
            logger.error(f"format_exc: {error_info}")
            raise SeleniumError(f"UI test error: {e}")
        finally:
            logger.info("脚本执行完成！")
    return wrapper


# 装饰器用法示例
@common_test_prepare
def demo_test_prepare(input_data=None, ui_expect=None):
    logger.info("测试装饰器成功！")


def open_menu_nk(input_data=None, ui_expect=None):
    '''
    打卡菜单。 '融资融券-查询统计-合约明细'
    :param input_data:
        {'菜单名称': '信用业务-融资融券-查询统计-合约明细'} 或者 {'菜单名称': '合约明细'}
    :param ui_expect:
    :return:
    '''
    # 打开子系统
    menu_name = input_data.get('菜单名称', None)
    menu_list = menu_name.split('-')
    logger.info(f"打开菜单{menu_name}")
    if len(menu_list) > 1:
        time.sleep(0.5)
        open_child_system(menu_list[0])
        FE.menu().select_third(menu_list[1:])
        time.sleep(0.5)
    else:
        raise SeleniumError(f"菜单名称格式错误，请输入类似'信用业务-融资融券-查询统计-合约明细'的菜单名称")
    # 打开页面后，检查是否存在弹框
    msgBox = FE.msg_box(idname={By.CSS_SELECTOR: 'div.h-message'}, timeout=1)
    flag = msgBox.exist()
    if flag:
        content = msgBox.get_text()
        raise SeleniumError(f"检查出现错误弹框，内容为：{content}")


def open_scene_detail(scene_name: str):
    """
    监控中心首页打开指标场景详情。
    :param scene_name:
    :return:
    """
    scenes = scene_name.split('-')
    if len(scenes) != 2:
        err_2 = f"入参错误，`风险项名称`格式错误！"
        logger.error(err_2)
        raise SeleniumError(err_2)
    scene_first, scene_second = scenes[0], scenes[1]
    # 场景名称筛选
    left_xpath = f'//div[@class="scene-cate-info-wrap"]//div[contains(text(),"{scene_first}")]'
    left_ele_list = FE.button(idname={By.XPATH: left_xpath})
    left_ele_list.click()
    time.sleep(0.5)
    input_ele = FE.input_type(idname={'xpath': '(//div[@class="home-page-search-item-value"])[2]/div'})
    input_ele.set_value(scene_second)
    input_ele.click_search()
    time.sleep(0.5)
    # 点击场景名称
    liEleXpath = (f'//div[@class="header-line-title"]'
                  f'/span[contains(text(),"{scene_first}")]//ancestor::div[@class="scene-info-list-wrap"]')
    selectLists = FE.driver.find_elements_by_attribute(By.XPATH, liEleXpath, timeout=1)
    if len(selectLists) > 0:
        text_xpath = f'.//div[@class="table-body-td-text"]/div/a[contains(text(),"{scene_second}")]'
        for ele in selectLists:
            if ele.is_displayed():
                target_ele = ele.find_element_by_xpath(text_xpath)
                target_ele.click()  # 模糊匹配会匹配到多的，所以需要去点击当前下拉框可见的
                logger.info(f"点击场景：{scene_name}")
                break
    else:
        err_3 = f"未找到场景：{scene_name}"
        logger.error(err_3)
        raise SeleniumError(err_3)


if __name__ == '__main__':
    input_data = {'is_blade_platform': False}
    demo_test_prepare(input_data=input_data, ui_expect=None)